//
//  EZCDataBase.cpp
//  sqlitepersistentobjectsTest
//
//  Created by ellzu on 13-7-30.
//  Copyright (c) 2013年 ellzu. All rights reserved.
//

#include "EZCDataBase.h"
#include <map>
#include "Factory.h"
#include "EZCDBTestModel.h"

using namespace EZCDBNS;

EZCDataBase::EZCDataBase():
_database(0),_filePatch("")
{
    
}

EZCDataBase::~EZCDataBase()
{
    CloseDatabase();
    
    for (std::map<std::string,EZCDBTableInfo*>::iterator it=tableMap.begin(); it!=tableMap.end();it++) {
        it->second->Release();
    }
    
}
sqlite3* EZCDataBase::databaseHandel()
{
    return _database;
}
std::string EZCDataBase::getDataBaseFilePath()
{
    return _filePatch;
}
int EZCDataBase::setDataBaseFilePath(const std::string &filePatch)
{
    if (_filePatch.compare(filePatch)==0) {
        return 1;
    }
    
    _filePatch = filePatch;
    
    int code = OpenDatabase();
    if (code!=0) {
        _filePatch.assign("");
    }
    return code;
}

int EZCDataBase::OpenDatabase()
{
    if (_filePatch.length() == 0) {
        return -1;
    }
    
    CloseDatabase();
    
    if (sqlite3_open(_filePatch.c_str(), &_database) != SQLITE_OK)
    {
        sqlite3_close(_database);
        return -2;
    }
    
    return 0;
}

int EZCDataBase::CloseDatabase()
{
    if (_database==0) {
        return 1;
    }
    sqlite3_close(_database);
    _database = 0;
    return 0;
}

void EZCDataBase::RegistTable(EZCDBTableInfo *info)
{
    tableMap[info->classIdName] = info;
    EZCDBObject *obj = (EZCDBObject*)NewObject(info->classIdName);
    info->tableFilds = obj->DatabaseField();
//    delete obj;
    obj->Release();
    CreateDBTable(info);
}


void EZCDataBase::CreateDBTable(EZCDBTableInfo* tableinfo)
{
    std::string sql("Create table if not exists ");
    sql.append(tableinfo->tableName);
    sql.append("(");
    
    for (int i=0; i<tableinfo->tableFilds.size(); i++) {
        EZCDBFieldInfo *fieldInfo = tableinfo->tableFilds.at(i);
        char cfield[100];
        memset(cfield, 0, 100);
        sprintf(cfield, "%s %s %s %s",fieldInfo->dbFieldName.c_str(),fieldInfo->dbTypename.c_str(),fieldInfo->dbIdentify.c_str(),i<tableinfo->tableFilds.size()-1?",":");");
        sql.append(cfield);//名字　类型　标识 ,or);
    }
    
    if (sqlite3_exec(_database, sql.c_str(), NULL, NULL, NULL)!=SQLITE_OK) {
        std::cout<<"create table fail:"<<tableinfo->className <<std::endl;
    }else {
        std::cout<<"create table success model:"<< tableinfo->className << "to tableName:"<< tableinfo->tableName <<std::endl;
    }
    
}


int EZCDataBase::SaveObject(EZCDBObject *object)
{
    EZCDBTableInfo *tableInfo = TableInfoWithIdName(typeid(*object).name());
    
    std::string insertSql = tableInfo->getInsertSql();
    
    sqlite3_stmt *stmt;
    int code = sqlite3_prepare_v2(_database, insertSql.c_str(), -1, &stmt, 0);
    if ( code ==SQLITE_OK) {
        for (int i=0; i<tableInfo->tableFilds.size(); i++) {
            EZCDBFieldInfo *fieldInfo = tableInfo->tableFilds.at(i);
            void *voidvalue = ( object->*(fieldInfo->gvFunc) )();
            switch (fieldInfo->dbtype) {
                case EZCDataBase_int:
                {
                    int *value = (int*)voidvalue;
                    sqlite3_bind_int(stmt, i+1, *value );
                }
                    break;
                case EZCDataBase_float:
                {
                    double *value = (double*)voidvalue;
                    sqlite3_bind_double(stmt, i+1, *value);
                }
                    break;
                case EZCDataBase_text:
                {
                    std::string *value = (std::string*)voidvalue;
                    sqlite3_bind_text(stmt, i+1, value->c_str(), -1, NULL);
                }
                    break;
                case EZCDataBase_binary:
                {
                    sqlite3_bind_blob(stmt, i+1, voidvalue, -1, NULL);
                }
                    break;
                case EZCDataBase_datetime:
                {
                    double *value = (double*)voidvalue;
                    sqlite3_bind_double(stmt,i+1,*value);
                }
                    break;
                default:
                    break;
            }
        }
    }
    code = sqlite3_step(stmt);
    if (code!=SQLITE_DONE) {
//        NSLog(@"insert error .....");
        std::cout << "insert error objtype:"<<tableInfo->className<<std::endl;
    }else{
        code = 0;
    }
    sqlite3_finalize(stmt);
    
    return code;
}
EZTally::EZTallyAutoPtr EZCDataBase::FindObjectsOne(EZCDBObject *fobject,int *errorCode,bool fuzzy)
{
    EZTally::EZTallyArray arr = FindObjects(fobject,2, errorCode,fuzzy);
    EZTally::EZTallyAutoPtr autoPtr;
    if (arr.Count()>0) {
        EZCDBObject *object = 0;
        object = (EZCDBObject*)arr.ObjectAtIndex(0);
        autoPtr.Set(object,true);
    }
    return autoPtr;
}
EZTally::EZTallyArray EZCDataBase::FindObjects(EZCDBObject *fobject,int maxcount,int *errorCode,bool fuzzy )
{

    EZTally::EZTallyArray arr;
    EZCDBTableInfo *tableInfo = TableInfoWithIdName(typeid(*fobject).name());
    
    std::string findSql("SELECT * FROM ");
    findSql += tableInfo->tableName;
    
    std::string wheresql= MakeWhereString(fobject,fuzzy);
    findSql.append(wheresql);
    
    if (maxcount>0) {
        char wsql[20];
        memset(wsql, 0, 20);
        sprintf(wsql, " limit %d",maxcount);
        findSql.append(wsql);
    }
    sqlite3_stmt *stmt;
    int code = sqlite3_prepare_v2(_database, findSql.c_str(), -1, &stmt, 0);
    if (code==SQLITE_OK) {
        
        while (sqlite3_step(stmt)==SQLITE_ROW) {
            
            EZCDBObject *modelFind = (EZCDBObject*)NewObject(tableInfo->classIdName);
            
            for (int i=0; i<tableInfo->tableFilds.size(); i++) {
                EZCDBFieldInfo *fieldInfo = tableInfo->tableFilds.at(i);
                const void *voidValue = 0;
//                id value = [EZDataBase EZTypeValue:field.fieldType andStmt:stmt andIndex:i];
//                [modelFind setValue:value forKey:field.modelName];
                switch (fieldInfo->dbtype) {
                    case EZCDataBase_int:
                    {
                        int value = sqlite3_column_int(stmt, i);
                        voidValue = &value;
                        (modelFind->*fieldInfo->svFunc)(voidValue);
                    }
                        break;
                    case EZCDataBase_float:
                    {
                        float value = sqlite3_column_double(stmt, i);
                        voidValue = &value;
                        (modelFind->*fieldInfo->svFunc)(voidValue);
                    }
                        break;
                    case EZCDataBase_binary:
                    {
                        const void *value = sqlite3_column_blob(stmt, i);
                        voidValue = value;
                        (modelFind->*fieldInfo->svFunc)(voidValue);
                    }
                        break;
                    case EZCDataBase_text:
                    {
                        const unsigned char *value = sqlite3_column_text(stmt,i);
                        if (value==0) {
                            voidValue = "";
                        }else{
                            voidValue = value;
                        }
                        (modelFind->*fieldInfo->svFunc)(voidValue);
                    }
                        break;
                    case EZCDataBase_datetime:
                    {
                        double value = sqlite3_column_double(stmt, i);
                        voidValue = &value;
                        (modelFind->*fieldInfo->svFunc)(voidValue);
                    }
                        break;
                    default:
                        break;
                }
            }
            
            arr.AddObject(modelFind);
            modelFind->Release();
        }
    }
    
    sqlite3_finalize(stmt);
    
    if (errorCode) {
        *errorCode = 0;
    }
    return arr;
}
int EZCDataBase::ClearTable(const std::string &tableIdName,int *delRow)
{
    EZCDBTableInfo *tableInfo = TableInfoWithIdName(tableIdName);
    if (tableInfo==0) {
        return -1;
    }
    return RemoveObjectWithWhereStr(tableInfo->tableName, "",delRow);
}
int EZCDataBase::RemoveObject(EZCDBObject *object,int *delRow,bool fuzzy)
{
    EZCDBTableInfo *tableInfo = TableInfoWithIdName(typeid(*object).name());
    if (tableInfo==0) {
        return -1;
    }
    std::string wheresql= MakeWhereString(object,fuzzy);
    if (wheresql.length()==0) {
        return -2;
    }
    return RemoveObjectWithWhereStr(tableInfo->tableName, wheresql,delRow);
}
int EZCDataBase::RemoveObjectWithWhereStr(const std::string &tableName,const std::string &wheresql,int *delRow)
{
    std::string delSql("DELETE FROM ");
    delSql += tableName;
    delSql += wheresql;
    int code = 0;
    sqlite3_stmt *stmt;
    code = sqlite3_prepare_v2(_database,delSql.c_str(), -1, &stmt, 0);
    if (code==SQLITE_OK) {
         code = sqlite3_step(stmt);
        if (code==SQLITE_DONE) {
            code = 0;
            if (delRow!=0) {
                *delRow = sqlite3_changes(_database);
            }
        }
    }
    sqlite3_finalize(stmt);
    
    
    return code;
}
std::string EZCDataBase::MakeWhereString(EZCDBObject *object,bool fuzzy)
{
    
    
    std::string whereSql(" WHERE ");
    
    EZCDBTableInfo *tableInfo = TableInfoWithIdName(typeid(*object).name());
    if (tableInfo==0) {
        return "";
    }
    
    for (int i=0; i<tableInfo->tableFilds.size(); i++) {
        EZCDBFieldInfo *fieldInfo = tableInfo->tableFilds.at(i);
        void *voidvalue = ( object->*(fieldInfo->gvFunc) )();
        if (whereSql.length()>8) {
            whereSql.append(" AND ");
        }
        switch (fieldInfo->dbtype) {
            case EZCDataBase_int:
            {
                int *value = (int*)voidvalue;
                if (*value!=int_NotOnFindValue) {
                    char wsql[256];
                    memset(wsql, 0, 256);
                    sprintf(wsql, " %s = %d ",fieldInfo->dbFieldName.c_str(),*value);
                    whereSql.append(wsql);
                }
            }
                break;
                //            case EZCDataBase_float:
                //            {
                //                double *value = (double*)voidvalue;
                //
                //            }
                break;
            case EZCDataBase_text:
            {
                std::string *value = (std::string*)voidvalue;
                char *wsql = new char[value->length() + 128];
                memset(wsql, 0, value->length()+128);
                const char *fuzzychar = fuzzy==true?"%":"";
                sprintf(wsql, " %s like '%s%s%s' ",fieldInfo->dbFieldName.c_str(),fuzzychar,value->c_str(),fuzzychar);
                
                whereSql.append(wsql);
                
                delete wsql;
            }
                break;
                //            case EZCDataBase_binary:
                //            {
                //
                //            }
                //                break;
                //            case EZCDataBase_datetime:
                //            {
                //                double *value = (double*)voidvalue;
                //
                //            }
                //                break;
            default:
                break;
        }
    }
    
    if (whereSql.length() <= 8 ) {
        whereSql.assign("");
    }
    return whereSql;
}

EZCDBTableInfo* EZCDataBase::TableInfoWithIdName(const std::string &idName)
{
    if (tableMap.find(idName)==tableMap.end()) {
        return 0;
    }
    EZCDBTableInfo *tableInfo = tableMap[idName];
    return tableInfo;
}




